using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using ChessKit.Common;
using ChessKit.Ui.ChessPanel;
using Vector = System.Windows.Vector;

namespace ChessKit.Ui
{
  public class MasterBoardPanel : Panel
  {
    #region IsSharedLayoutScope

    public static readonly DependencyProperty IsSharedLayoutScopeProperty =
      DependencyProperty.RegisterAttached("IsSharedLayoutScope", typeof (bool), typeof (MasterBoardPanel),
                                          new PropertyMetadata(default(bool)));

    public static void SetIsSharedLayoutScope(UIElement element, bool value)
    {
      element.SetValue(IsSharedLayoutScopeProperty, value);
    }

    public static bool GetIsSharedLayoutScope(UIElement element)
    {
      return (bool) element.GetValue(IsSharedLayoutScopeProperty);
    }

    #endregion

    #region ' Transform : IBoardLocationTransform (Dependency Property) '

    public IBoardLocationTransform Transform
    {
      get { return (IBoardLocationTransform)GetValue(TransformProperty); }
      set { SetValue(TransformProperty, value); }
    }

    public static readonly DependencyProperty TransformProperty =
      DependencyProperty.Register("Transform", typeof(IBoardLocationTransform),
      typeof(MasterBoardPanel), new FrameworkPropertyMetadata(BoardTransform.Identity,
        FrameworkPropertyMetadataOptions.AffectsMeasure,
        OnTransformPropertyChanged, CoerceTransform));

    private static void OnTransformPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ((MasterBoardPanel)d).OnTransformPropertyChanged((IBoardLocationTransform)e.NewValue);
    }
    private void OnTransformPropertyChanged(IBoardLocationTransform value)
    {
      _layout.Transform = value;
    }

    private static object CoerceTransform(DependencyObject d, object basevalue)
    {
      return basevalue ?? BoardTransform.Identity;
    }

    #endregion

    #region ' Measure & Arrange '

    private readonly Layout _layout = new Layout();

    protected override Size MeasureOverride(Size availableSize)
    {
      var infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);

      foreach (var element in InternalChildren.OfType<UIElement>())
      {
        switch (BoardPanel.GetLocation(element).Area)
        {
          case BoardArea.Background:
            element.Measure(availableSize);
            break;
          case BoardArea.Unknown:
            element.Measure(new Size());
            break;
          default:
            element.Measure(infiniteSize);
            break;
        }
      }

      _layout.Measure(InternalChildren.OfType<UIElement>(), availableSize);
      foreach (var slaveBoardPanel in FindSlaves())
        slaveBoardPanel.Layout = _layout;
      return _layout.DesiredSize;
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
      _layout.AdjustMeasures(arrangeSize);

      foreach (var element in InternalChildren.OfType<UIElement>())
      {
        var location = BoardPanel.GetLocation(element);
        if (location != BoardLocation.Unknown)
        {
          element.Arrange(_layout[location]);
        }
        else
        {
          var locations = BoardPanel.GetLocations(element);
          if (locations == null) continue;
          var rects = locations.Select(l => _layout[l]).ToList();
          var envelope = rects.Aggregate(Rect.Union);
          element.Arrange(envelope);
          var boarded = element as IBoardedElement;
          if (boarded == null) continue;
          boarded.SetCells(rects.Select(r => RelativeTo(r, envelope)));
        }
      }
      return arrangeSize;
    }

    private static Rect RelativeTo(Rect inner, Rect outer)
    {
      return new Rect(inner.TopLeft - (Vector)outer.TopLeft, inner.Size);
    }

    #endregion

    private IEnumerable<SlaveBoardPanel> FindSlaves()
    {
      var p = this.FindVisualParent<UIElement>(GetIsSharedLayoutScope);
      if (p == null) return Enumerable.Empty<SlaveBoardPanel>();
      return p.FindVisualChildren<SlaveBoardPanel>();
    }
  }
}